\subsection{Пример \#3: MS-DOS}
\label{dongle_16bit_dos}

\myindex{MS-DOS}
Еще одна очень старая программа для MS-DOS от 1995 
также разработанная давно исчезнувшей компанией.

\myindex{Intel!8086}
\myindex{Intel!80286}
Во времена перед DOS-экстендерами, всё ПО для MS-DOS рассчитывалось на процессоры 8086 или 80286,
так что в своей массе весь код был 16-битным.
16-битный код в основном такой же, какой вы уже видели в этой книге, но все регистры 16-битные,
и доступно меньше инструкций.

\label{IN_example}
\label{OUT_example}
\myindex{x86!\Instructions!IN}
\myindex{x86!\Instructions!OUT}
Среда MS-DOS не могла иметь никаких драйверов, и ПО работало с \q{голым} железом через порты,
так что здесь вы можете увидеть инструкции \TT{OUT}/\TT{IN}, 
которые в наше время присутствуют в основном только
в драйверах (в современных OS нельзя обращаться на прямую к портам из \gls{user mode}).

Учитывая это, ПО для MS-DOS должно работать с донглой обращаясь к принтерному LPT-порту
напрямую.
Так что мы можем просто поискать эти инструкции. И да, вот они:

\lstinputlisting[style=customasmx86]{examples/dongles/3/1.lst}

(Все имена меток в этом примере даны мною).

Функция \TT{out\_port()} вызывается только из одной функции:

\lstinputlisting[style=customasmx86]{examples/dongles/3/2.lst}

Это также \q{хеширующая} донгла Sentinel Pro как и в предыдущем примере.
Это заметно по тому что текстовые строки передаются и здесь, 16-битные значения также возвращаются и сравниваются с другими.

Так вот как происходит работа с Sentinel Pro через порты.
Адрес выходного порта обычно 0x378, т.е. принтерного порта, данные для него во времена
перед USB отправлялись прямо сюда.
Порт однонаправленный, потому что когда его разрабатывали, никто не мог предположить,
что кому-то понадобится получать информацию из принтера
\footnote{Если учитывать только Centronics и не учитывать последующий стандарт IEEE 1284 --- в нем
из принтера можно получать информацию.}.
Единственный способ получить информацию из принтера это регистр статуса на порту 0x379,
он содержит такие биты как \q{paper out}, \q{ack}, \q{busy} --- так принтер может сигнализировать
о том, что он готов или нет, и о том, есть ли в нем бумага.
Так что донгла возвращает информацию через какой-то из этих бит, по одному биту на каждой
итерации.

\TT{\_in\_port\_2} содержит адрес статуса (0x379) и 
\TT{\_in\_port\_1} содержит адрес управляющего регистра (0x37A).

Судя по всему, донгла возвращает информацию только через флаг \q{busy} на \TT{seg030:00B9}: 
каждый бит записывается в регистре \TT{DI} позже возвращаемый в самом конце функции.

Что означают все эти отсылаемые в выходной порт байты?
Трудно сказать. Возможно, команды донглы.
Но честно говоря, нам и не обязательно знать: нашу задачу можно легко решить и без этих знаний.

Вот функция проверки донглы:

\lstinputlisting[style=customasmx86]{examples/dongles/3/3.lst}

А так как эта функция может вызываться слишком часто, например, 
перед выполнением каждой важной возможности ПО,
а обращение к донгле вообще-то медленное (и из-за медленного принтерного порта, и из-за медленного
\ac{MCU} в донгле), так что они, наверное, добавили возможность пропускать проверку донглы слишком часто,
используя текущее время в функции \TT{biostime()}.

\myindex{\CStandardLibrary!rand()}
Функция \TT{get\_rand()} использует стандартную функцию Си:

\lstinputlisting[style=customasmx86]{examples/dongles/3/4.lst}

Так что текстовая строка выбирается случайно, отправляется в донглу и результат
хеширования сверяется с корректным значением.

Текстовые строки, похоже, составлялись так же случайно, во время разработки ПО.

И вот как вызывается главная процедура проверки донглы:

\lstinputlisting[style=customasmx86]{examples/dongles/3/5.lst}

Заставить работать программу без донглы очень просто: просто заставить функцию
\TT{check\_dongle()} возвращать всегда 0.

Например, вставив такой код в самом её начале:

\begin{lstlisting}[style=customasmx86]
mov ax,0
retf
\end{lstlisting}

\myindex{\CStandardLibrary!strcpy()}
Наблюдательный читатель может заметить, что функция Си \TT{strcpy()} имеет 2 аргумента, но здесь
мы видим, что передается 4:

\begin{lstlisting}[style=customasmx86]
seg033:088F 1E                          push    ds
seg033:0890 68 22 44                    push    offset aTrupcRequiresA ; "This Software Requires a Software Lock\n"
seg033:0893 1E                          push    ds
seg033:0894 68 60 E9                    push    offset byte_6C7E0 ; dest
seg033:0897 9A 79 65 00+                call    _strcpy
seg033:089C 83 C4 08                    add     sp, 8
\end{lstlisting}

Это связано с моделью памяти в MS-DOS. Об этом больше читайте здесь: 
\myref{8086_memory_model}.

Так что, \TT{strcpy()}, как и любая другая функция принимающая указатель (-и) в аргументах,
работает с 16-битными парами.

Вернемся к нашему примеру.
\TT{DS} сейчас указывает на сегмент данных размещенный в исполняемом файле, там, где хранится текстовая строка.

\myindex{x86!\Instructions!LES}
В функции \TT{sent\_pro()} каждый байт строки загружается на\\
\TT{seg030:00EF}: инструкция
\TT{LES} загружает из переданного аргумента пару ES:BX одновременно.
\MOV на \TT{seg030:00F5} загружает байт из памяти, на который указывает пара ES:BX.

% TODO rewrite
%На \TT{seg030:00F2} \glslink{increment}{инкрементируется} только вторая 16-битная пара адреса.
%
%Это значит, что переданная в функцию строка не может находиться на границе двух сегментов.
%

